﻿using BoriPeter.FatSecret;
using BoriPeter.FitnessJournal8.Models;
using BoriPeter.NutritionFileSource;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace BoriPeter.FitnessJournal8.DAL
{
    public enum FoodService
    {
        All,
        History,
        Favorite,
        NewEntry,
        Nutrition
    };

    public enum EntryService
    {
        All,
        Weight,
        CaloriesIntake,
        CaloriesBurned,
        Foods,
        Exercises
    };

    public class NutritionDataAbstraction
    {
        private AllFoodFileSource allFoodFS;
        private HistoryFoodFileSource historyFoodFS;
        private FatSecretFoodDataSource fatSecretFoodDS;
        private FatSecretWeightDataSource fatSecretWeightDS;
        private FatSecretDailyIntakeDataSource fatSecretDailyIntakeDS;
        private FatSecretDailyBurnedDataSource fatSecretDailyBurnedDS;

        private List<DailyEntry> DailyEntryList;
        private DateTime DailyEntryWeightFrom;
        private DateTime DailyEntryIntakeFrom;
        private DateTime DailyEntryBurnedFrom;

        #region FoodList
        private List<Food> foodList;
        public List<Food> FoodList
        {
            get { return foodList; }
            set { foodList = value; }
        }
        #endregion

        #region Constructor
        public NutritionDataAbstraction()
        {
            Reset();
        }
        #endregion

        #region GetByExpressionAsync
        public async Task<Food> GetByExpressionAsync(Func<Food,bool> _exp, FoodService _service = FoodService.Nutrition)
        {
            if (FoodList == null)
            {
                FoodList = await LoadFoodListAsync();
            }
            Food food = FoodList.Where(_exp).FirstOrDefault();
            if (food != null)
            {
                switch (_service)
                {
                    case FoodService.All : break;
                    default:  
                        {
                            if (food.Servings == null)
                            {
                                Food food2 = await fatSecretFoodDS.GetByIdAsync(food.Id);
                                food = food.Merge(food2);
                            }
                        } break;
                }
            }
            return food;
        }
        #endregion

        #region GetByIdAsync
        public async Task<Food> GetByIdAsync(int _id, FoodService _service = FoodService.Nutrition)
        {
            return await GetByExpressionAsync(f => f.Id == _id, _service);
        }
        #endregion

        #region GetListAsync()
        public async Task<List<Food>> GetListAsync(string _searchExp, FoodService _service = FoodService.All, int _maximum = 5000)
        {
            Func<Food, bool> exp = (f => f.Name.ToLower().Contains(_searchExp.ToLower()) ||
                f.Brand.ToLower().Contains(_searchExp.ToLower()));

            return await GetListAsync(exp, _service, _maximum);
        }

        public async Task<List<Food>> GetListAsync(FoodService _service = FoodService.All)
        {
            return await GetListAsync(f => true, _service);
        }

        public async Task<List<Food>> GetListAsync(Func<Food, bool> _exp,
            FoodService _service = FoodService.All, int _maximum = 5000)
        {
            if (FoodList == null)
            {
                FoodList = await LoadFoodListAsync();
            }
            if (FoodList != null)
            {
                switch (_service)
                {
                    case FoodService.All: return FoodList.Where(_exp).Take(_maximum).ToList();
                    case FoodService.History:
                        {
                            return FoodList.Where(f => f.VisitedFrequency > 0).Where(_exp).Take(_maximum).ToList();
                        }
                    case FoodService.Favorite:
                        {
                            return FoodList.Where(f => f.Favorite == true).Where(_exp).Take(_maximum).ToList();
                        }
                    default: return FoodList;
                }
            }
            return new List<Food>();
        }
        #endregion

        #region GetDailyEntryListAsync
        public async Task<List<DailyEntry>> GetDailyEntryListAsync(DateTime _dateFrom, EntryService _service = EntryService.All)
        {
            DateTime startDate, endDate, actualDate;

            /* **** Request missing WEIGHT entries ****** */
            if ((_service == EntryService.All) || (_service == EntryService.Weight))
            {
                if (_dateFrom < DailyEntryWeightFrom)
                {
                    startDate = DailyEntryWeightFrom.AddMonths(-1);
                    endDate = _dateFrom.FirstDayOfMonth();
                    actualDate = startDate;

                    while (actualDate >= endDate)
                    {
                        List<DailyEntry> list = await fatSecretWeightDS.GetMonthListAsync(actualDate);
                        foreach (DailyEntry item in list)
                            DailyEntryList.Merge(item);
                        actualDate = actualDate.AddMonths(-1);
                    }

                    DailyEntryWeightFrom = _dateFrom.FirstDayOfMonth();
                }
            }

            /* **** Request missing CALORIES INTAKE entries ****** */
            if ((_service == EntryService.All) || (_service == EntryService.CaloriesIntake))
            {
                if (_dateFrom < DailyEntryIntakeFrom)
                {
                    startDate = DailyEntryIntakeFrom.AddMonths(-1);
                    endDate = _dateFrom.FirstDayOfMonth();
                    actualDate = startDate;

                    while (actualDate >= endDate)
                    {
                        List<DailyEntry> list = await fatSecretDailyIntakeDS.GetMonthListAsync(actualDate);
                        foreach (DailyEntry item in list)
                            DailyEntryList.Merge(item);
                        actualDate = actualDate.AddMonths(-1);
                    }

                    DailyEntryIntakeFrom = _dateFrom.FirstDayOfMonth();
                }
            }

            /* **** Request missing CALORIES BURNED entries ****** */
            if ((_service == EntryService.All) || (_service == EntryService.CaloriesBurned))
            {
                if (_dateFrom < DailyEntryBurnedFrom)
                {
                    startDate = DailyEntryBurnedFrom.AddMonths(-1);
                    endDate = _dateFrom.FirstDayOfMonth();
                    actualDate = startDate;

                    while (actualDate >= endDate)
                    {
                        List<DailyEntry> list = await fatSecretDailyBurnedDS.GetMonthListAsync(actualDate);
                        foreach (DailyEntry item in list)
                            DailyEntryList.Merge(item);
                        actualDate = actualDate.AddMonths(-1);
                    }

                    DailyEntryBurnedFrom = _dateFrom.FirstDayOfMonth();
                }
            }



            return DailyEntryList;
        }
        #endregion

        #region LoadFoodListAsync
        private async Task<List<Food>> LoadFoodListAsync()
        {
            List<Food> listAll = await allFoodFS.GetAllAsync();
            List<Food> listHistory = await historyFoodFS.GetAllAsync();
            List<Food> listFatSecret = await fatSecretFoodDS.GetAllAsync();

            listAll = listAll.Merge(listHistory).Merge(listFatSecret);

            return listAll;
        }
        #endregion

        #region SaveAllAsync()
        public async Task SaveAllAsync()
        {
            await historyFoodFS.SaveAllAsync(FoodList);
        }
        #endregion

        #region SaveAsync()
        public async Task SaveAsync(Food _food)
        {
            await fatSecretFoodDS.SaveAsync(_food);
        }

        public async Task SaveAsync(DailyEntry _entry)
        {
            if (_entry.WeightKg != 0)
                await fatSecretWeightDS.SaveAsync(_entry);
        }
        #endregion

        #region AddFood2History()
        public void AddFood2History(Food _food)
        {
            _food.VisitedFrequency++;
            _food.LastAccessedDate = DateTime.Now;
        }
        #endregion

        #region GetDailyEntryAsync
        public async Task<DailyEntry> GetDailyEntryAsync(DateTime _date)
        {
            DailyEntry entryReturned;
            if (DailyEntryList.Where(e => e.Date.DayEqual(_date)).Count() > 0)
                entryReturned = DailyEntryList.Where(e => e.Date.DayEqual(_date)).First();
            else
            {
                entryReturned = new DailyEntry() { Date = _date };
            }

            if (entryReturned.FoodEntries == null)
            {
                var newEntry = await fatSecretDailyIntakeDS.GetDailyEntryAsync(_date, FoodList);
                entryReturned.Merge(newEntry);
            }

            if (entryReturned.ExerciseEntries == null)
            {
                var newEntry = await fatSecretDailyBurnedDS.GetDailyEntryAsync(_date);
                entryReturned.Merge(newEntry);
            }

            return entryReturned;
        }
        #endregion

        #region Reset()
        public void Reset()
        {
            allFoodFS = new AllFoodFileSource("foods.xml");
            historyFoodFS = new HistoryFoodFileSource("history.xml");
            fatSecretFoodDS = new FatSecretFoodDataSource();
            fatSecretWeightDS = new FatSecretWeightDataSource();
            fatSecretDailyIntakeDS = new FatSecretDailyIntakeDataSource();
            fatSecretDailyBurnedDS = new FatSecretDailyBurnedDataSource();

            DailyEntryList = new List<DailyEntry>();
            DailyEntryWeightFrom = DateTime.Now.FirstDayOfMonth().AddMonths(1);
            DailyEntryIntakeFrom = DateTime.Now.FirstDayOfMonth().AddMonths(1);
            DailyEntryBurnedFrom = DateTime.Now.FirstDayOfMonth().AddMonths(1);
        }
        #endregion
    }
}
